#include <fuquOS/memory.h>
#include <fuquOS/printk.h>
#include <fuquOS/stdio.h>

// 设计带有头节点的可用物理内存链表
// 空闲内存链表
PhysicalMemoryLink *pml = NULL;
// 已分配内存链表
PhysicalMemoryLink *assigned_pml = NULL;
void memory_init(void)
{
  u32 ARD_COUNT = *(u32 *)0x7900;
  MemoryDescriptor *ARD = (MemoryDescriptor *)0x8000;
  pml = (PhysicalMemoryLink *)malloc(sizeof(PhysicalMemoryLink));
  if (!pml)
  {
    printk_err("Failed to allocate memory for pml\n");
    return;
  }
  PhysicalMemoryLink *temp = pml;
  temp->data = NULL;
  temp->next = NULL;
  for (int i = 1; i < ARD_COUNT; i++)
  {
    printk("%d addr high %x,low %x;len high %x,low %x;type %d\n", i, ARD[i].base_addr_high, ARD[i].base_addr_low, ARD[i].length_high, ARD[i].length_low, ARD[i].type);
    if (ARD[i].type == 1)
    {
      PhysicalMemoryBlock *tempMemoryBlock =
          (PhysicalMemoryBlock *)malloc(sizeof(PhysicalMemoryBlock));
      if (!tempMemoryBlock)
      {
        printk_err("Failed to allocate memory for tempMemoryBlock\n");
        return;
      }
      tempMemoryBlock->base_addr_low = ARD[i].base_addr_low;
      tempMemoryBlock->base_addr_high = ARD[i].base_addr_high;
      tempMemoryBlock->length_low = ARD[i].length_low;
      tempMemoryBlock->length_high = ARD[i].length_high;
      PhysicalMemoryLink *tempNode =
          (PhysicalMemoryLink *)malloc(sizeof(PhysicalMemoryLink));
      if (!tempNode)
      {
        printk_err("Failed to allocate memory for tempMemoryBlock\n");
        return;
      }
      tempNode->data = tempMemoryBlock;
      tempNode->next = NULL;
      temp->next = tempNode;
      temp = tempNode;
    }
  }
  assigned_pml = (PhysicalMemoryLink *)malloc(sizeof(PhysicalMemoryLink));
}

void *kmalloc(u32 size)
{
  if (size == 0)
  {
    return NULL;
  }

  PhysicalMemoryLink *prev = NULL;
  PhysicalMemoryLink *cur = pml->next;
  while (cur)
  {
    u32 block_size = cur->data->length_low;

    if (block_size >= size)
    {
      if (block_size == size)
      {
        if (prev)
        {
          prev->next = cur->next;
        }
        else
        {
          pml = cur->next;
        }

        cur->next = assigned_pml->next;
        assigned_pml->next = cur;

        // 32 位系统只需要使用 base_addr_low
        return (void *)(cur->data->base_addr_low);
      }

      u32 new_base_low = cur->data->base_addr_low + size;
      u32 carry = (new_base_low < cur->data->base_addr_low) ? 1 : 0;
      u32 new_base_high = cur->data->base_addr_high + carry;

      u32 new_length_low = cur->data->length_low - size;
      carry = (cur->data->length_low < size) ? 1 : 0;
      u32 new_length_high = cur->data->length_high - carry;

      cur->data->base_addr_low = new_base_low;
      cur->data->base_addr_high = new_base_high;
      cur->data->length_low = new_length_low;
      cur->data->length_high = new_length_high;

      PhysicalMemoryBlock *new_block =
          (PhysicalMemoryBlock *)malloc(sizeof(PhysicalMemoryBlock));
      new_block->base_addr_low = cur->data->base_addr_low;
      new_block->base_addr_high = 0; // 32 位系统无需关心高 32 位
      new_block->length_low = size;
      new_block->length_high = 0; // length_high 为 0

      PhysicalMemoryLink *allocated =
          (PhysicalMemoryLink *)malloc(sizeof(PhysicalMemoryLink));
      allocated->data = new_block;
      allocated->next = assigned_pml->next;
      assigned_pml->next = allocated;

      // 返回 32 位地址
      return (void *)(new_block->base_addr_low);
    }

    prev = cur;
    cur = cur->next;
  }
  return NULL;
}

void kfree(void *ptr)
{
  if (!ptr)
  {
    return; // 如果指针为空，不做任何操作
  }

  PhysicalMemoryLink *prev = NULL;
  PhysicalMemoryLink *cur = assigned_pml->next;
  PhysicalMemoryLink *prev_free = NULL;
  PhysicalMemoryLink *cur_free = pml->next;

  // 遍历已分配内存链表，找到需要释放的内存块
  while (cur)
  {
    u32 block_addr = cur->data->base_addr_low;

    if (block_addr == (u32)ptr)
    {
      // 找到要释放的内存块
      if (prev)
      {
        prev->next = cur->next; // 将其从链表中移除
      }
      else
      {
        assigned_pml->next = cur->next; // 如果是链表头节点，更新头指针
      }

      // 将该内存块加入空闲内存链表
      cur->next = pml->next; // 将当前节点的 next 指向空闲内存链表
      pml->next = cur;       // 更新空闲内存链表的头节点

      // 合并相邻的内存块
      while (cur_free)
      {
        // 检查当前空闲块与释放块是否相邻
        u32 free_block_addr = cur_free->data->base_addr_low;
        u32 free_block_end = free_block_addr + cur_free->data->length_low;

        u32 cur_block_addr = cur->data->base_addr_low;
        u32 cur_block_end = cur_block_addr + cur->data->length_low;

        // 如果空闲块与释放块相邻，进行合并
        if (free_block_end == cur_block_addr)
        {
          // 合并到空闲块的后面
          cur_free->data->length_low += cur->data->length_low;
          cur_free->data->length_high += cur->data->length_high;

          // 释放当前块
          PhysicalMemoryLink *temp = cur;
          cur = cur->next;
          break;
        }
        else if (cur_block_end == free_block_addr)
        {
          // 合并到当前空闲块的前面
          cur_free->data->base_addr_low = cur->data->base_addr_low;
          cur_free->data->base_addr_high = cur->data->base_addr_high;
          cur_free->data->length_low += cur->data->length_low;
          cur_free->data->length_high += cur->data->length_high;

          // 释放当前块
          PhysicalMemoryLink *temp = cur;
          cur = cur->next;
          break;
        }

        prev_free = cur_free;
        cur_free = cur_free->next;
      }

      return;
    }

    prev = cur;
    cur = cur->next;
  }

  // 如果没有找到匹配的内存块，可以在这里处理错误（例如打印日志）
  printk_err("Attempted to free an unallocated block\n");
}
